package BasicModel;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;

import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.RDFNode;

import BasicModel.Element.*;
import BasicModel.Element.Component.*;
import Manage.ModelMgr;

public class AtomicModel {
	// model name
	String name;
	String componentName;
	public String getComponentName() {
		return componentName;
	}

	public String getName() {
		return name;
	}

	// IO-port parameters state
	LinkedList<SimpleVariable> paraList = new LinkedList<SimpleVariable>();
	InputSet inputSet = new InputSet();
	OutputSet outputSet = new OutputSet();
	ParamSet yieldSet = new ParamSet();
	ParamSet optFlowrateSet = new ParamSet();


	public ParamSet getYieldSet() {
		return yieldSet;
	}

	public ParamSet getOptFlowrateSet() {
		return optFlowrateSet;
	}

	// StateSet has included the PhaseVariable
	StateSet stateSet = new StateSet();

	// Functions
	//	InitialFunction init_func;
	//	InternalTransitionFunction intTransFunc = new InternalTransitionFunction();
	//	ExternalTransitionFunction extTransFunc = new ExternalTransitionFunction();
	//	OutputFunction outputFunc = new OutputFunction();
	//	TimeAdvanceFunction timeAdvFunc;

	//
	String constructFuncContext = "";
	String internalFuncContext = "";
	String externalFuncContext = "";
	String initFuncContext = "";
	String outputFuncContext = "";

	public AtomicModel(String modelName, String componentName, OntModel owlModel) {

		this.name = modelName;
		this.componentName = componentName;
		/*
		 * query the inputPort
		 */
		String queryStr = ModelMgr.prefix + "SELECT ?inPort ?portVar "
				+ "Where {devsmo:" + this.name + " devsmo:isComposedOf ?inPort. "
				+ "?inPort rdf:type devsmo:InputPort. "
				+ "?portVar devsmo:refersToPort ?inPort}";
		QueryExecution qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode inPort_x = soln.get("inPort");
				RDFNode portVar_x = soln.get("portVar");
				Individual inPort_Ins = owlModel.getIndividual(inPort_x
						.toString());
				Individual portVar_Ins = owlModel.getIndividual(portVar_x
						.toString());
				inputSet.addPort(new Port(inPort_Ins.getLocalName(),
						"float", portVar_Ins.getLocalName()));
			}
		} finally {
			qexec.close();
		}

		/*
		 * query the outPort
		 */
		queryStr = ModelMgr.prefix + "SELECT ?outPort ?portVar "
				+ "Where {devsmo:" + this.name + " devsmo:isComposedOf ?outPort. "
				+ "?outPort rdf:type devsmo:OutputPort. "
				+ "?portVar devsmo:refersToPort ?outPort}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode outPort_x = soln.get("outPort");
				RDFNode portVar_x = soln.get("portVar");
				Individual inPort_Ins = owlModel.getIndividual(outPort_x
						.toString());
				Individual portVar_Ins = owlModel.getIndividual(portVar_x
						.toString());
				//				String temp = varType_x.toString().toLowerCase();
				outputSet.addPort(new Port(inPort_Ins.getLocalName(),
						"float", portVar_Ins
						.getLocalName()));
			}
		} finally {
			qexec.close();
		}


		/*
		 * query the yield
		 */
		queryStr = ModelMgr.prefix + "SELECT ?param ?value "
				+ "Where {devsmo:" + this.name + " devsmo:isComposedOf ?param . "
				+ "?param rdf:type devsmo:YieldParameter ."
				+ "?param devsmo:valueOfParameter ?value}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);		
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode paramNode = soln.get("param");
				RDFNode paramValueNode = soln.get("value");
				Individual param_Ins = owlModel.getIndividual(paramNode
						.toString());
				yieldSet.addparam(new Param("float", param_Ins.getLocalName(), paramValueNode.toString()));
			}
		} finally {
			qexec.close();
		}


		/*
		 * query the optFlowrate
		 */
		queryStr = ModelMgr.prefix + "SELECT ?param ?value "
				+ "Where {devsmo:" + this.name + " devsmo:isComposedOf ?param . "
				+ "?param rdf:type devsmo:OptFlowrateParameter ."
				+ "?param devsmo:valueOfParameter ?value}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);		
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode paramNode = soln.get("param");
				RDFNode paramValueNode = soln.get("value");
				Individual param_Ins = owlModel.getIndividual(paramNode
						.toString());
				optFlowrateSet.addparam(new Param("float", param_Ins.getLocalName(), paramValueNode.toString()));
			}
		} finally {
			qexec.close();
		}

		
		/*
		 * query the constructFunction
		 */
		queryStr = ModelMgr.prefix + "SELECT ?prog ?context ?func " + 
				"where{devsmo:" + this.name + " devsmo:isComposedOf ?func . " +
				"?func rdf:type devsmo:ConstructFunction . " +
				"?func devsmo:hasRule ?prog . " +
				"?prog devsmo:ruleContext ?context}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode constructFuncNode = soln.get("context");
				constructFuncContext = constructFuncNode.toString();

			}
		} finally {
			qexec.close();
		}
		
		/*
		 * query the InitFunction
		 */
		queryStr = ModelMgr.prefix + "SELECT ?prog ?context ?func " + 
				"where{devsmo:" + this.name + " devsmo:isComposedOf ?func . " +
				"?func rdf:type devsmo:InitFunction . " +
				"?func devsmo:hasRule ?prog . " +
				"?prog devsmo:ruleContext ?context}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode InitFuncNode = soln.get("context");
				initFuncContext = InitFuncNode.toString();

			}
		} finally {
			qexec.close();
		}
		

		/*
		 * query the internalFunction
		 */
		queryStr = ModelMgr.prefix + "SELECT ?prog ?context ?func " + 
				"where{devsmo:" + this.name + " devsmo:isComposedOf ?func . " +
				"?func rdf:type devsmo:InternalTransitionFunction . " +
				"?func devsmo:hasRule ?prog . " +
				"?prog devsmo:ruleContext ?context}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode internalFuncNode = soln.get("context");
				internalFuncContext = internalFuncNode.toString();

			}
		} finally {
			qexec.close();
		}


		/*
		 * query the externalFunction
		 */
		queryStr = ModelMgr.prefix + "SELECT ?prog ?context ?func " + 
				"where{devsmo:" + this.name + " devsmo:isComposedOf ?func . " +
				"?func rdf:type devsmo:ExternalTransitionFunction . " +
				"?func devsmo:hasRule ?prog . " +
				"?prog devsmo:ruleContext ?context}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode externalFuncNode = soln.get("context");
				externalFuncContext = externalFuncNode.toString();

			}
		} finally {
			qexec.close();
		}


		/*
		 * query the OutputFunction
		 */
		queryStr = ModelMgr.prefix + "SELECT ?prog ?context ?func " + 
				"where{devsmo:" + this.name + " devsmo:isComposedOf ?func . " +
				"?func rdf:type devsmo:OutputFunction . " +
				"?func devsmo:hasRule ?prog . " +
				"?prog devsmo:ruleContext ?context}";
		qexec = QueryExecutionFactory.create(queryStr, owlModel);
		try {
			ResultSet results = qexec.execSelect();
			for (; results.hasNext();) {
				QuerySolution soln = results.nextSolution();
				RDFNode outputFuncNode = soln.get("context");
				outputFuncContext = outputFuncNode.toString();

			}
		} finally {
			qexec.close();
		}

		

	}

	/*
	 * create a "name.cpp" file in the path.
	 */
	public void WriteCpp(File path) throws FileNotFoundException {
		if (!path.exists()) {
			path.mkdirs();
		}
		String pathStr = null;
		try {
			pathStr = path.getCanonicalPath();
		} catch (IOException e) {
			e.printStackTrace();
		}
		String fileStr = pathStr + "/" + this.name + ".cpp";
		File cppFile = new File(fileStr);

		// create a empty file.if exists() delete it
		if (cppFile.exists()) {
			cppFile.delete();
		}
		try {
			cppFile.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		PrintWriter out = new PrintWriter(cppFile);

		out.println("#include <string>");
		out.println("#include \"atomic.h\"");
		out.println("#include \"message.h\"");
		out.println("#include \"mainsimu.h\"");
		out.println("#include \"strutil.h\"");
		out.printf("#include \"../HeadFile/%s.h\"\n", this.name);
		out.println();
		out.println();

		/*
		 * constructor
		 */
		out.println("/**** the Constructor ****/");
		out.printf("%s::%s( const string &name)\n", this.name, this.name);
		out.println(": Atomic( name )");

		// add input port
		if (this.inputSet.getInputPortSet() != null) {
			Iterator<Port> it = this.inputSet.getInputPortSet().iterator();
			while (it.hasNext()) {
				String portName = it.next().getPortName();
				out.printf(", %s( this->addInputPort( \"%s\") )\n", portName,
						portName);
			}
		}

		// add output port
		if (this.outputSet.getOutputPortSet() != null) {
			Iterator<Port> it = this.outputSet.getOutputPortSet().iterator();
			while (it.hasNext()) {
				String portName = it.next().getPortName();
				out.printf(", %s( this->addOutputPort( \"%s\") )\n", portName,
						portName);
			}
		}

		out.println("{");
		out.println(constructFuncContext);
		out.println("}");
		out.println();

		/*
		 * initFunction
		 */
		out.println();
		out.println("/*** the initFunction ***/");
		out.printf("Model &%s::initFunction()\n", this.name);
		out.println("{");
		out.println(initFuncContext);
		out.println("}");

		/*
		 * externalFunction
		 */
		out.println();
		out.println("/*** the externalFunction ***/");
		out.printf(
				"Model &%s::externalFunction( const ExternalMessage &msg )\n",
				this.name);
		out.println("{");
		out.println(externalFuncContext);
		out.println("}");

		/*
		 * internalFunction
		 */
		out.println();
		out.println("/*** the internalFunction ***/");
		out.printf(
				"Model &%s::internalFunction( const InternalMessage &msg )\n",
				this.name);
		out.println("{");
		out.println(internalFuncContext);
		out.println("}");

		/*
		 * outputFunction
		 */
		out.println();
		out.println("/*** the outputFunction ***/");
		out.printf("Model &%s::outputFunction( const InternalMessage &msg )\n",
				this.name);
		out.println("{");
		out.println(outputFuncContext);
		out.println("}");

		out.println();
		out.close();

	}

	public void WriteJava(File path) throws FileNotFoundException {
		if (!path.exists()) {
			path.mkdirs();
		}
		String pathStr = null;
		try {
			pathStr = path.getCanonicalPath();
		} catch (IOException e) {
			e.printStackTrace();
		}
		String fileStr = pathStr + "/" + this.name + ".java";
		File javaFile = new File(fileStr);

		// create a empty file.if exists() delete it
		if (javaFile.exists()) {
			javaFile.delete();
		}
		try {
			javaFile.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		PrintWriter out = new PrintWriter(javaFile);

		out.println("package com.ontology.rglab;");
		out.println();
		out.println("import com.cd.devs.Atomic;");
		out.println();
		out.printf("public class %s extends Atomic\n", this.name);
		out.println("{");

		// all input_port
		for (Port in_port : this.inputSet.getInputPortSet()) {
			out.printf("\tprivate const Port %s;\n", in_port.getPortName());
			out.printf("\t%s %s;\n", in_port.getSignalVar().getValueType(),
					in_port.getSignalVar().getName());
		}
		out.println();
		// all output_port
		for (Port out_port : this.outputSet.getOutputPortSet()) {
			out.printf("\tprivate Port %s;\n", out_port.getPortName());
			out.printf("\t%s %s;\n", out_port.getSignalVar().getValueType(),
					out_port.getSignalVar().getName());
		}
		out.println();


		// all yield
		for (Param yield : this.yieldSet.getparamSet()) {
			out.printf("\tprivate %s %s;\n", yield.getSignalVar().getValueType(),
					yield.getSignalVar().getName());
		}
		out.println();

		// all optFlowrate
		for (Param optFlowrate : this.optFlowrateSet.getparamSet()) {
			out.printf("\tprivate %s %s;\n", optFlowrate.getSignalVar().getValueType(),
					optFlowrate.getSignalVar().getName());
		}
		out.println();

		out.println("\tprivate Time delay;");
		out.println("\tprivate float total_flowrate;");
		out.println("\tprivate int count;");

		out.println();


		/*
		 * externalFunction
		 */
		out.println();
		out.println("/*** the externalFunction ***/");
		out.println("public Model externalFunction(ExternalMessage msg)");
		out.println("{");
		out.println(externalFuncContext.replaceAll("const", "").replaceAll("&", "").replace("*", ""));
		out.println("}");

		/*
		 * internalFunction
		 */
		out.println();
		out.println("/*** the internalFunction ***/");
		out.println("public Model internalFunction(InternalMessage msg)");
		out.println("{");
		out.println(internalFuncContext.replaceAll("const", "").replaceAll("&", "").replace("*", ""));
		out.println("}");

		/*
		 * outputFunction
		 */
		out.println();
		out.println("/*** the outputFunction ***/");
		out.println("public Model outputFunction(InternalMessage msg)\n");
		out.println("{");
		out.println(outputFuncContext.replaceAll("const", "").replaceAll("&", "").replace("*", ""));
		out.println("}");

		out.println();
		out.println("}");
		out.println();
		out.close();

	}
	/*
	 * create the *h file in the path
	 */
	public void WriteH(File path) throws FileNotFoundException {

		if (!path.exists()) {
			path.mkdirs();
		}
		String pathStr = null;
		try {
			pathStr = path.getCanonicalPath();
		} catch (IOException e) {
			e.printStackTrace();
		}
		String fileStr = pathStr + "/" + this.name + ".h";
		File HFile = new File(fileStr);
		// create a empty file. if exists() delete it
		if (HFile.exists()) {
			HFile.delete();
		}
		try {
			HFile.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		PrintWriter out = new PrintWriter(HFile);

		out.printf("#ifndef _%s_H\n", this.name.toUpperCase());
		out.printf("#define _%s_H\n", this.name.toUpperCase());
		out.println("#include \"atomic.h\"");
		out.println();

		out.printf("class %s : public Atomic\n", this.name);
		out.println("{");


		// public constructor function
		out.println("public:");
		String defClassName = this.name;

		out.printf("\t%s( const string & name = \"%s\" );\n", defClassName, defClassName);
		out.printf("\tinline string className() const\n");
		out.println("\t{");
		out.printf("\t\treturn \"%s\" ;\n", defClassName);
		out.println("\t}");
		out.println();

		// protected 4 functions
		out.println("protected:");
		out.println("\tModel &initFunction();");
		out.println("\tModel &externalFunction(const ExternalMessage &);");
		out.println("\tModel &internalFunction(const InternalMessage &);");
		out.println("\tModel &outputFunction(const InternalMessage &);");
		out.println();

		// private field
		out.println("private:");
		// all input_port
		for (Port in_port : this.inputSet.getInputPortSet()) {
			out.printf("\tconst Port &%s;\n", in_port.getPortName());
			out.printf("\t%s %s;\n", in_port.getSignalVar().getValueType(),
					in_port.getSignalVar().getName());
		}
		out.println();
		// all output_port
		for (Port out_port : this.outputSet.getOutputPortSet()) {
			out.printf("\tPort &%s;\n", out_port.getPortName());
			out.printf("\t%s %s;\n", out_port.getSignalVar().getValueType(),
					out_port.getSignalVar().getName());
		}
		out.println();


		// all yield
		for (Param yield : this.yieldSet.getparamSet()) {
			out.printf("\t%s %s;\n", yield.getSignalVar().getValueType(),
					yield.getSignalVar().getName());
		}
		out.println();

		// all optFlowrate
		for (Param optFlowrate : this.optFlowrateSet.getparamSet()) {
			out.printf("\t%s %s;\n", optFlowrate.getSignalVar().getValueType(),
					optFlowrate.getSignalVar().getName());
		}
		out.println();

		out.println("\tTime delay;");
		out.println("\tfloat total_flowrate;");
		out.println("\tint count;");
		out.println("};");
		out.println();
		out.println("#endif");
		out.close();
	}

}
